﻿using System.IO;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.ComponentModel.Design;
using Microsoft.Win32;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.SharePoint;
using Microsoft.VisualStudio;
using EnvDTE;
using EnvDTE80;
using EnvDTE90;
using EnvDTE100;
using SPVSX.DeploymentExtensions;
using SPVSX.Common;

namespace SPVSX.DeploymentExtensions.VSPackage
{
    /// <summary>
    /// Base VS Package for custom SharePoint deployment functionality.
    /// 
    /// Though this code is Open Source, in good faith it is requested that you consult the original author(s)
    /// before changing or re-using it for any public endeavour intended to provide similar functionality, or
    /// to fix simple bugs.  In other words, if you want to create SPVSX2, please let us know first.
    /// </summary>
    /// <remarks>@author Matt Smith, http://blog.mattsmith.co.nz</remarks>
    
    // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is a package.
    [PackageRegistration(UseManagedResourcesOnly = true)]
    // This attribute is used to register the informations needed to show the this package
    // in the Help/About dialog of Visual Studio.
    [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
    // This attribute is needed to let the shell know that this package exposes some menus.
    [ProvideMenuResource("Menus.ctmenu", 1)]
    // This attribute is needed to ensure our package is loaded as soon as a solution exists (and thus the Quick Deploy menu handlers can be registered).
    [ProvideAutoLoad(GuidList.guidUIContextSolutionExistsString)]
    [Guid(GuidList.guidSPVSX_Extensions_PackagePkgString)]
    public sealed class SPVSXPackage : Package, IVsShellPropertyEvents
    {
        bool hasInitialised = false;

        #region Constructor

        /// <summary>
        /// Default constructor of the package.
        /// Inside this method you can place any initialization code that does not require 
        /// any Visual Studio service because at this point the package object is created but 
        /// not sited yet inside Visual Studio environment. The place to do all the other 
        /// initialization is the Initialize method.
        /// </summary>
        public SPVSXPackage()
        {
            Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this.ToString()));
        }

        #endregion

        #region Package Members

        /// <summary>
        /// Initialization of the package; this method is called right after the package is sited, so this is the place
        /// where you can put all the initilaization code that rely on services provided by VisualStudio.
        /// </summary>
        protected override void Initialize()
        {
            Trace.WriteLine (string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString()));
            base.Initialize();

            // Added by MattSmithNZ
            // Cache the SP project service for reuse as necessary.
            // We will always have a SP project, otherwise SPVSX will not work at all and should not have been installed.
            this.SharePointProjectService = this.GetService(typeof(ISharePointProjectService)) as ISharePointProjectService;
            
            // Added by MattSmithNZ
            // Call out to our event manager to register and handle project events.
            this.EventManager = new EventHandlerManager(this);
            this.EventManager.RegisterHandlers();

            // Added by MattSmithNZ
            // Workaround DTE not being available until startup is complete by receiving notification when the shell is loaded.
            IVsShell shellService = GetService(typeof(SVsShell)) as IVsShell;
            if (shellService != null)
            {
                shellService.AdviseShellPropertyChanges(this, out dteCookie);
            }

            // Call to ensure the DTE stuff is awake
            InitialiseDTEDependantObjects();
        }

        #endregion

        #region Properties

        internal object GetServiceInternal(Type type)
        {
            return this.GetService(type);
        }

        internal ISharePointProjectService SharePointProjectService
        {
            get;
            set;
        }

        internal OutputWindow OutputWindow
        {
            get
            {
                EnvDTE80.DTE2 dte2 = GetService(typeof(SDTE)) as EnvDTE80.DTE2;
                return dte2.ToolWindows.OutputWindow;
            }
        }

        internal EventHandlerManager EventManager
        {
            get;
            set;
        }

        #endregion

        #region IVsShellPropertyEvents Members

        // Cached copies of event handler objects to avoid COM destroying our handlers.
        private uint dteCookie;
        private uint projTrackCookie;
        
        public int OnShellPropertyChange(int propid, object var)
        {
            // When zombie state changes to false, finish package initialization.
            if ((int)__VSSPROPID.VSSPROPID_Zombie == propid)
            {
                if ((bool)var == false)
                {
                    InitialiseDTEDependantObjects();

                    // Zombie state clean-up code.
                    IVsShell shellService = GetService(typeof(SVsShell)) as IVsShell;
                    if (shellService != null)
                    {
                        shellService.UnadviseShellPropertyChanges(this.dteCookie);
                        shellService.UnadviseShellPropertyChanges(this.projTrackCookie);
                    }
                    this.dteCookie = 0;
                    this.projTrackCookie = 0;
                }

            }

            return VSConstants.S_OK;
        }

        private void InitialiseDTEDependantObjects()
        {
            if (!hasInitialised)
            {
                EnvDTE80.DTE2 dte2 = GetService(typeof(SDTE)) as EnvDTE80.DTE2;

                if (dte2 != null)
                {
                    // Zombie state dependent code.
                    dte2 = GetService(typeof(SDTE)) as EnvDTE80.DTE2;
                    EnvDTE80.Events2 events = (EnvDTE80.Events2)dte2.Events;
                    EnvDTE.DTE dte = GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE;

                    // Register event handlers that need the DTE active to do work.
                    EventManager.RegisterDteDependentHandlers(dte2, events);

                    // Initialize the output logger.  This is a bit hacky but a quick fix for Alpha release.
                    OutputWindowLogger.InitializeInstance(dte2);
                    StatusBarLogger.InitializeInstance(dte);
                    hasInitialised = true;
                }
            }
        }

        #endregion
    }
}
